home *** CD-ROM | disk | FTP | other *** search
-
- #pragma segment TAR
-
- /*
- * Macintosh Tar
- *
- * Modifed by Craig Ruff for use on the Macintosh.
- */
- /*
- * Create a tar archive.
- *
- * Written 25 Aug 1985 by John Gilmore, ihnp4!hoptoad!gnu.
- *
- * @(#)create.c 1.19 9/9/86 Public Domain - gnu
- */
-
- #include "tar.h"
- #include "stat.h"
- #include <string.h>
-
- union record *StartHeader();
- extern union record *head;
- extern struct
- {
- long st_size;
- long st_mtime;
- } hstat; /* Fake stat struct for compat. */
-
- void FinishHeader();
- void ToOct();
- Boolean DumpDir(), DumpFile(), FillName(), WriteEot();
-
- /*
- * Used to save pathname info while descending the directory hierarchy.
- */
- struct PathInfo {
- struct PathInfo *next;
- char name[32];
- };
- typedef struct PathInfo PathInfo;
- PathInfo pathHead;
-
- /*
- * ArCreate - manage the creation of an archive
- *
- * Asks for the archive name, creates the archive and then
- * loops asking for directories to add to the archive.
- */
-
- #ifdef TCLAPPL
-
- ArCreate()
- {
- Boolean errFound = false;
- Point where;
- SFReply reply;
- CInfoPBRec pb;
- CursHandle cursor;
- Str255 name;
-
- /*TGE*/ extern WindowPtr theFeedbackWindow;
- /*TGE*/ extern short feedback_showing;
- /*TGE*/ extern short in_back_ground;
-
- /*
- * Put up a standard file dialog asking for the archive file name.
- */
- where.h = where.v = 75;
- name[0] = 0;
- MyPutFile(where, "\pTar Archive:", name, nil, &reply);
- if (!reply.good)
- return;
-
- arName = reply.fName;
- WDDirVRef(reply.vRefNum, &arVRefNum, &arDirID);
-
- if (OpenArchive(0))
- {
- /* Open for writing */
- WPrintf("Open Archive Failed.");
- return;
- }
-
- /*TGE*/ UBegYield();
-
- /*
- * Ask for directories to add to the archive.
- * Note that this is WHOLE directories.
- */
- while (!errFound)
- {
- /*TGE*/ if (in_back_ground)
- while (in_back_ground)
- /*TGE*/ pausing();
-
- if (! GetFolderPathName("Directory To Archive:", name, &dirVRefNum, &dirDirID))
- break;
- #ifdef NEVER_DEFINED
- if (! GetDir("\pDirectory to Archive:", false))
- break;
- #endif
-
- /*TGE*/ ShowFeedback();
-
- /*
- * Get the catalog info for the selected directory.
- */
- pathHead.next = nil;
- pathHead.name[0] = '\0';
- pb.hFileInfo.ioCompletion = nil;
- pb.hFileInfo.ioNamePtr = pathHead.name;
- pb.hFileInfo.ioVRefNum = dirVRefNum;
- pb.hFileInfo.ioDirID = dirDirID;
- pb.hFileInfo.ioFDirIndex = -1;
- if (PBGetCatInfo(&pb, false) != noErr)
- {
- OSAlert("\pArCreate", "\pPBGetCatInfo", pathHead.name,
- pb.hFileInfo.ioResult);
- break;
-
- }
- else
- {
- /*
- * Add the directory to the archive,
- * while printing the files being added.
- */
- if (WindInit())
- goto done;
-
- /*TGE*/ SetPort(theFeedbackWindow);
- TextFace(underline);
- WPrintf(header);
- /*TGE*/ SetPort(theFeedbackWindow);
- TextFace(0);
- if ((cursor = GetCursor(watchCursor)) != nil)
- SetCursor(*cursor);
-
- errFound = DumpDir(&pb, &pathHead);
-
- SetCursor(&qd.arrow);
- FlushEvents(everyEvent, 0);
- }
-
- /*TGE*/ /*HideFeedback();*/
- }
-
- /*TGE*/ ShowFeedback();
-
- WriteEot();
-
- done:
-
- CloseArchive();
-
- WPrintf("--- tar creation completed.");
- /*TGE*/
- UEndYield();
- UInitCursor();
- }
-
- #endif /* TCLAPPL */
-
-
- Cmd_Archive(clientData, interp, argc, argv)
- char *clientData;
- Tcl_Interp *interp;
- int argc;
- char *argv[];
- {
- Boolean errFound = false, save_cvtNl;
- int tarArgc, arg_index, myerr, result = TCL_OK;
- char **tarArgv, *name;
- Tcl_DString tildeBuf;
- CInfoPBRec pb;
- CursHandle cursor;
- char *ptr, *default_pathhead = NULL, *archive_arg;
- Str255 arname, itemname;
- PathInfo subPath, *dirPathPtr, filepath;
- struct stat statbuf;
-
- #pragma unused (clientData)
-
- extern WindowPtr theFeedbackWindow;
- extern short feedback_showing;
- extern short in_back_ground;
-
- if (argc < 3)
- {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " ?-p prefix? archive_filename filelist\"", (char *) NULL);
- result = TCL_ERROR;
- }
-
- save_cvtNl = cvtNl;
- cvtNl = false;
- default_pathhead = NULL;
-
- for ( arg_index = 1 ; arg_index < argc && ! errFound ; ++arg_index )
- {
- if (argv[arg_index][0] != '-')
- break;
-
- if (argv[arg_index][1] == '-' && argv[arg_index][2] == '\0')
- break;
-
- if (argv[arg_index][1] == 'p' && argv[arg_index][2] == '\0')
- {
- default_pathhead = argv[arg_index+1];
- ++arg_index;
- }
- else if (argv[arg_index][1] == 'a' && argv[arg_index][2] == '\0')
- {
- cvtNl = true;
- }
- else
- break;
- }
-
- if (arg_index >= argc)
- {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " ?-p prefix? archive_filename filelist\"", (char *) NULL);
- result = TCL_ERROR;
- }
-
- archive_arg = argv[arg_index++];
- ptr = strrchr( archive_arg, ':' );
- if ( *archive_arg == ':' || ptr == NULL )
- {
- arDirID = TclMac_CWDDirID();
- arVRefNum = TclMac_CWDVRefNum();
- strcpy(arname, (ptr == NULL ? archive_arg : ptr + 1));
- }
- else
- {
- *ptr = '\0';
- myerr = stat( archive_arg, &statbuf );
- if ( myerr < 0 )
- {
- Tcl_AppendResult(interp, "could not locate directory \"", archive_arg,
- "\" to create archive - ", Tcl_PosixError(interp), NULL);
- *ptr = ':';
- result = TCL_ERROR;
- }
- if ( ! S_ISDIR(statbuf.st_mode) )
- {
- Tcl_AppendResult(interp, "\"", archive_arg, "\" is not a directory", NULL);
- *ptr = ':';
- result = TCL_ERROR;
- }
- *ptr = ':';
-
- arDirID = statbuf.st_ino;
- arVRefNum = statbuf.st_dev;
- strcpy(arname, ptr + 1);
- }
- c2pstr(arname);
- arName = arname;
-
- if (arg_index >= argc)
- {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " ?-p prefix? archive_filename filelist\"", (char *) NULL);
- result = TCL_ERROR;
- }
-
- if ( Tcl_SplitList(interp, argv[arg_index], &tarArgc, &tarArgv) != TCL_OK )
- {
- cvtNl = save_cvtNl;
- return TCL_ERROR;
- }
-
- tar_scripting = 1;
- tar_interp = interp;
-
- if (OpenArchive(0)) /* Open for writing */
- {
- Tcl_AppendResult(interp, "could not open \"", argv[arg_index-1],
- "\" to write archive into", (char *) NULL);
- tar_scripting = 0;
- tar_interp = NULL;
- cvtNl = save_cvtNl;
- return TCL_ERROR;
- }
-
- UBegYield();
-
- pathHead.next = NULL;
- pathHead.name[0] = 1;
- pathHead.name[1] = '.';
-
- if (default_pathhead != NULL)
- {
- if (*default_pathhead == '/' && *(default_pathhead + 1) == '\0')
- {
- pathHead.name[0] = '\0';
- }
- else
- {
- strcpy(pathHead.name, default_pathhead);
- c2pstr(pathHead.name);
- }
- }
-
- /*
- * Ask for directories to add to the archive.
- * Note that this is WHOLE directories.
- */
- for ( arg_index = 0 ; arg_index < tarArgc && result == TCL_OK ; ++arg_index )
- {
- if (in_back_ground)
- while (in_back_ground)
- pausing();
-
- Tcl_DStringInit (&tildeBuf);
- name = Tcl_TildeSubst(interp, tarArgv[arg_index], &tildeBuf);
- if (name == NULL)
- {
- Tcl_AppendResult(interp, "could not substitute for directory \"",
- tarArgv[arg_index], "\" ", (char *) NULL);
- continue;
- }
-
- if (tcl_path_to_dir(name, &dirVRefNum, &dirDirID) != noErr)
- {
- Tcl_AppendResult(interp, "could not locate \"", name,
- "\" for archival", (char *) NULL);
- continue;
- }
-
- ShowFeedback();
-
- /*
- * Get the catalog info for the selected directory.
- */
- itemname[0] = '\0';
-
- subPath.next = NULL;
-
- ptr = strrchr(name, ':');
- if (ptr != NULL)
- {
- /* Has path elements */
- if ( *(ptr + 1) == '\0' )
- {
- /* Directory only -> ":dir:dir:dir:" */
- }
- else
- {
- /* Directory or File -> ":dir:dir:item" */
- strcpy(itemname, ptr + 1);
- c2pstr(itemname);
- }
- }
- else
- {
- /* Has NO path elements */
- /* Directory or File -> "item" */
- strcpy(itemname, name);
- c2pstr(itemname);
- }
-
- pb.hFileInfo.ioCompletion = 0;
- pb.hFileInfo.ioVRefNum = dirVRefNum;
- pb.hFileInfo.ioDirID = dirDirID;
- pb.hFileInfo.ioNamePtr = itemname;
- pb.hFileInfo.ioFDirIndex = itemname[0] ? 0 : -1;
-
- if (PBGetCatInfo(&pb, false) != noErr)
- {
- Tcl_AppendResult(interp, "could not get info for \"", name,
- "\"", (char *) NULL);
- }
- else
- {
- /*
- ** Add the item to the archive,
- ** while printing the files being added.
- */
- //if (WindInit())
- // goto done;
-
- SetPort(theFeedbackWindow);
- TextFace(underline);
- WPrintf(header);
- SetPort(theFeedbackWindow);
- TextFace(0);
-
- if ( (cursor = GetCursor(watchCursor)) != NULL )
- SetCursor(*cursor);
-
- if ( (pb.hFileInfo.ioFlAttrib & ioDirMask) != 0 )
- {
- dirPathPtr = &pathHead;
- if (itemname[0] != '\0')
- {
- BlockMove(itemname, subPath.name, itemname[0]+1);
- pathHead.next = &subPath;
- dirPathPtr = &subPath;
- }
- errFound = DumpDir(&pb, dirPathPtr);
- }
- else
- {
- BlockMove(itemname, subPath.name, itemname[0]+1);
- pathHead.next = &subPath;
-
- errFound = DumpFile(&pb);
-
- pathHead.next = nil;
- }
-
- if (errFound)
- {
- Tcl_AppendResult(interp, "error could not archive ",
- ( (pb.hFileInfo.ioFlAttrib & ioDirMask) != 0 ? "directory" : "file" ),
- " \"", name, "\"", (char *) NULL);
- result = TCL_ERROR;
- }
-
- SetCursor(&qd.arrow);
- FlushEvents(everyEvent, 0);
- }
-
- Tcl_DStringFree (&tildeBuf);
- pathHead.next = NULL;
- }
-
- ShowFeedback();
-
- if (result == TCL_OK)
- WriteEot();
-
- CloseArchive();
-
- ckfree ((char *) tarArgv);
-
- cvtNl = save_cvtNl;
- tar_scripting = 0;
- tar_interp = NULL;
-
- WPrintf("--- tar creation completed.");
-
- UEndYield();
- UInitCursor();
-
- return result;
- }
-
- /*
- * DumpDir - add a directory (possibly recursively) to the archive
- *
- * Exits via a longjmp on unrecoverable error
- * Returns normally otherwise
- */
- Boolean
- DumpDir(dir, path)
- CInfoPBRec *dir;
- PathInfo *path;
- {
- union record *header;
- int i;
- Boolean errFound = false;
- CInfoPBRec pb;
- PathInfo file;
- char *routine = "\pDumpDir";
- extern int cancel_current_op;
- extern short pause_op;
-
- /*
- WPrintf("DumpDir(%d, %d, <%.*s>) ENTER",
- dir->dirInfo.ioVRefNum, dir->dirInfo.ioDrDirID,
- dir->dirInfo.ioNamePtr[0], &dir->dirInfo.ioNamePtr[1]);
- */
-
- /*
- * Output directory header record with permissions
- * FIXME, do this AFTER files, to avoid R/O dir problems?
- * If Unix Std format, don't put / on end of dir name
- * If old archive format, don't write record at all.
- */
- if (!oldArch) {
- /*
- * If people could really read standard archives,
- * this should be: (FIXME)
- * header = start_header(f_standard? p: namebuf, statbuf);
- * but since they'd interpret LF_DIR records as
- * regular files, we'd better put the / on the name.
- */
- if ((header = StartHeader(dir)) == nil)
- return(true);
-
- if (standard)
- header->header.linkflag = LF_DIR;
-
- FinishHeader(header); /* Done with directory header */
- head = header;
- PrintHeader();
- }
-
- file.next = nil;
- path->next = &file;
-
- /*
- * Check all entries in the directory.
- * Add regular files, recurse on subdirectories.
- */
- file.name[0] = '\0';
- pb.hFileInfo.ioCompletion = nil;
- pb.hFileInfo.ioNamePtr = file.name;
- pb.hFileInfo.ioVRefNum = dir->dirInfo.ioVRefNum;
- for (i = 1; !errFound; i++) {
- /*TGE*/ DoYield();
- if (pause_op)
- while (pause_op)
- pausing();
- if (cancel_current_op)
- /*TGE*/ break;
-
- pb.hFileInfo.ioCompletion = nil;
- pb.hFileInfo.ioDirID = dir->dirInfo.ioDrDirID;
- pb.hFileInfo.ioFDirIndex = i;
- pb.hFileInfo.ioVRefNum = dir->dirInfo.ioVRefNum;
- if (PBGetCatInfo(&pb, false) != noErr) {
- if (pb.hFileInfo.ioResult == fnfErr)
- break;
-
- /*TGE*/ WPrintf("DumpDir pPBGetCatInfo(%d, %d, #%d) result = %d.",
- dir->dirInfo.ioVRefNum, dir->dirInfo.ioDrDirID, i, pb.hFileInfo.ioResult);
- OSAlert(routine, "\pPBGetCatInfo", "\pDirectory search",
- pb.hFileInfo.ioResult);
- return(true);
- }
-
- if ((unsigned char) file.name[0] > 32) {
- /*
- * Sanity check, we have overwritten our stack!
- */
- PgmAlert(routine, "\pName too long", file.name);
- return(true);
- }
-
- if (DIRECTORY(pb)) {
- errFound = DumpDir(&pb, &file);
-
- } else {
- if (pb.hFileInfo.ioFRefNum == archive) {
- /*
- * DO NOT add the archive to itself!
- */
- ArSkipAlert();
- continue;
- }
-
- errFound = DumpFile(&pb);
- }
-
- }
-
- /*
- WPrintf("DumpDir(%d, %d, <%.*s>) EXIT",
- dir->dirInfo.ioVRefNum, dir->dirInfo.ioDrDirID,
- dir->dirInfo.ioNamePtr[0], &dir->dirInfo.ioNamePtr[1]);
- */
-
- /*
- * Done with this directory, make sure we don't run out
- * of working directories.
- */
- path->next = nil;
- return(errFound);
- }
-
- /*
- * DumpFile - Dump a single file.
- *
- * Exits via longjmp on unrecoverable error.
- * Result is 1 for success, 0 for failure.
- */
- Boolean
- DumpFile(file)
- CInfoPBRec *file;
- {
- union record *header;
- register char *p;
- char *buf;
- HParamBlockRec fpb;
- long bufsize, count, i;
- register long sizeleft;
- register union record *start;
- char *routine = "\pDumpFile";
-
- if ((header = StartHeader(file)) == nil)
- return(true);
- FinishHeader(header);
-
- /*
- * Get the size of the file.
- * Don't bother opening it if it is zero length.
- */
- head = header;
- hstat.st_size = file->hFileInfo.ioFlLgLen;
- PrintHeader();
- if ((sizeleft = file->hFileInfo.ioFlLgLen) == 0)
- return(false);
-
- fpb.fileParam.ioCompletion = nil;
- fpb.fileParam.ioNamePtr = file->hFileInfo.ioNamePtr;
- fpb.fileParam.ioVRefNum = file->hFileInfo.ioVRefNum;
- fpb.fileParam.ioFVersNum = 0;
- fpb.fileParam.ioDirID = file->hFileInfo.ioFlParID;
- fpb.ioParam.ioPermssn = fsRdPerm;
- fpb.ioParam.ioMisc = nil;
- if (PBHOpen(&fpb, false) != noErr) {
- OSAlert(routine, "\pPBHOpen", file->hFileInfo.ioNamePtr,
- fpb.fileParam.ioResult);
- return(true);
- }
-
- /*
- * Dump the file to the archive.
- * Note: this only dumps the data fork!
- */
- while (sizeleft > 0) {
- if ((start = FindRec()) == nil)
- return(true);
-
- bufsize = EndOfRecs()->charptr - start->charptr;
- buf = start->charptr;
- again:
- count = (sizeleft < bufsize) ? sizeleft : bufsize;
- fpb.ioParam.ioBuffer = buf;
- fpb.ioParam.ioReqCount = count;
- fpb.ioParam.ioPosMode = fsAtMark;
- fpb.ioParam.ioPosOffset = 0;
- if (PBRead((ParmBlkPtr) &fpb, false) != noErr) {
- OSAlert(routine, "\pPBRead", file->hFileInfo.ioNamePtr,
- fpb.ioParam.ioResult);
- return(true);
- }
-
- count = fpb.ioParam.ioActCount;
- if (cvtNl) {
- /*
- * Convert returns to newlines for Unix compat.
- */
- for (i = count, p = buf; --i >= 0; p++)
- if (*p == RETURN)
- *p = LF;
- }
-
- sizeleft -= count;
- UseRec(start + (count - 1) / RECORDSIZE);
- }
-
- PBClose((ParmBlkPtr) &fpb, false);
-
- /* Clear last block garbage to zeros, FIXME */
- return(false);
- }
-
-
- /*
- * Make a header block for the file name whose stat info is st .
- * Return header pointer for success, NULL if the name is too long.
- */
- union record *
- StartHeader(pb)
- CInfoPBRec *pb;
- {
- register union record *header;
- Boolean directory = DIRECTORY(*pb);
-
- if ((header = (union record *) FindRec()) == nil)
- return(nil);
-
- bzero(header->charptr, sizeof(union record)); /* XXX speed up */
- /*
- * Generate the pathname, make sure we don't overflow
- * the field in the tar header.
- */
- if (FillName(header, directory)) {
- char buf[NAMSIZ + 1];
-
- buf[0] = NAMSIZ;
- memcpy(&buf[1], header->header.name, NAMSIZ);
- PgmAlert("\pStartHeader", "\pName too long", buf);
- return(nil);
- }
-
- /*
- * Fake the file mode, uid, gid.
- * Convert from Mac based time to Unix based time.
- */
- ToOct((directory) ? 0755L : 0644L, 8, header->header.mode);
- ToOct(0L, 8, header->header.uid);
- ToOct(0L, 8, header->header.gid);
- ToOct((directory) ? 0L : pb->hFileInfo.ioFlLgLen, 1+12,
- header->header.size);
- ToOct((directory) ? pb->dirInfo.ioDrMdDat - TIMEDIFF :
- pb->hFileInfo.ioFlMdDat - TIMEDIFF,
- 1+12, header->header.mtime);
- /* header->header.linkflag is left as null */
- return(header);
- }
-
- /*
- * Finish off a filled-in header block and write it out.
- */
- void
- FinishHeader(header)
- register union record *header;
- {
- register int i;
- register long sum;
- register char *p;
-
- memcpy(header->header.chksum, CHKBLANKS, sizeof(header->header.chksum));
-
- sum = 0;
- p = header->charptr;
- for (i = sizeof(union record); --i >= 0; ) {
- /*
- * We can't use unsigned char here because of old compilers,
- * e.g. V7.
- */
- sum += 0xFF & *p++;
- }
-
- /*
- * Fill in the checksum field. It's formatted differently
- * from the other fields: it has [6] digits, a null, then a
- * space -- rather than digits, a space, then a null.
- * We use to_oct then write the null in over to_oct's space.
- * The final space is already there, from checksumming, and
- * to_oct doesn't modify it.
- *
- * This is a fast way to do:
- * (void) sprintf(header->header.chksum, "%6o", sum);
- */
- ToOct((long) sum, 8, header->header.chksum);
- header->header.chksum[6] = '\0'; /* Zap the space */
- UseRec(header);
- return;
- }
-
-
- /*
- * Quick and dirty octal conversion.
- * Converts long "value" into a "digs"-digit field at "where",
- * including a trailing space and room for a null. "digs"==3 means
- * 1 digit, a space, and room for a null.
- *
- * We assume the trailing null is already there and don't fill it in.
- * This fact is used by start_header and finish_header, so don't change it!
- *
- * This should be equivalent to:
- * (void) sprintf(where, "%*lo ", digs-2, value);
- * except that sprintf fills in the trailing null and we don't.
- */
- void
- ToOct(value, digs, where)
- register long value;
- register int digs;
- register char *where;
- {
- --digs; /* Trailing null slot is left alone */
- where[--digs] = ' '; /* Put in the space, though */
-
- /* Produce the digits -- at least one */
- do {
- where[--digs] = '0' + (value & 7); /* one octal digit */
- value >>= 3;
- } while (digs > 0 && value != 0);
-
- /* Leading spaces, if necessary */
- while (digs > 0)
- where[--digs] = ' ';
-
- }
-
- /*
- * Write the EOT block(s).
- */
- Boolean
- WriteEot()
- {
- union record *p;
-
- if ((p = FindRec()) == nil)
- return(true);
-
- bzero(p->charptr, RECORDSIZE);
- UseRec(p);
- /* FIXME, only one EOT block should be needed. */
- if ((p = FindRec()) == nil)
- return(true);
-
- bzero(p->charptr, RECORDSIZE);
- UseRec(p);
- return(false);
- }
-
- /*
- * FillName - generate the file or directory pathname
- *
- * Converts to Unix style pathnames.
- * Appends a '/' for a directory.
- */
- Boolean
- FillName(header, directory)
- register union record *header;
- Boolean directory;
- {
- register PathInfo *p;
- register char *d, *s;
- char c;
- int i;
-
- d = header->header.name;
- for (p = &pathHead; p != nil; p = p->next) {
- s = &p->name[1];
- for (i = p->name[0]; i > 0; i--) {
- c = *s++;
- if (c == '/')
- *d++ = ':';
-
- else if ((c < ' ') || (c > '~'))
- *d++ = '_';
-
- else
- *d++ = c;
- }
-
- *d++ = (p->next == nil) ? '\0' : '/';
- }
-
- if (directory) {
- *(d - 1) = '/';
- *d = '\0';
- }
-
- return((d - header->header.name) > NAMSIZ);
- }
-